#!/usr/bin/env python3
"""
strategy.py - 交易策略定义和描述
- 包含所有策略函数（breakout, trend_following, 等）
- 提供 STRATEGY_DESCRIPTIONS（HTML 格式描述）
- 提供 STRATEGIES 字典（映射策略名称到函数）
- 每个策略生成 Signal（1=买入，-1=卖出）和 Sell_Reason 列
"""
import pandas as pd
import numpy as np

# ========= 策略描述 ========= #
STRATEGY_DESCRIPTIONS = {
    "breakout": """
        <h3>突破策略 (Breakout)</h3>
        <p><strong>买入信号</strong>：价格突破20日高点，且 RSI < 70，SMA10 > SMA20（确认趋势）。</p>
        <p><strong>卖出信号</strong>：
            <ul>
                <li>价格跌破 SMA10（<em>Below SMA10</em>）。</li>
                <li>单日价格涨幅超过15%（<em>Price Spike</em>）。</li>
            </ul>
        </p>
        <p><strong>参数</strong>：
            <ul>
                <li>SMA10：10日简单移动平均线</li>
                <li>SMA20：20日简单移动平均线</li>
                <li>High20：20日最高价</li>
                <li>RSI：14日相对强弱指数，阈值70</li>
            </ul>
        </p>
    """,
    "trend_following": """
        <h3>趋势跟随策略 (Trend Following)</h3>
        <p><strong>买入信号</strong>：SMA10 上穿 SMA50，且 RSI < 70。</p>
        <p><strong>卖出信号</strong>：
            <ul>
                <li>SMA10 下穿 SMA20（<em>SMA10 Below SMA20</em>）。</li>
                <li>RSI > 70（<em>RSI Overbought</em>）。</li>
            </ul>
        </p>
        <p><strong>参数</strong>：
            <ul>
                <li>SMA10：10日简单移动平均线</li>
                <li>SMA20：20日简单移动平均线</li>
                <li>SMA50：50日简单移动平均线</li>
                <li>RSI：14日相对强弱指数，阈值70</li>
            </ul>
        </p>
    """,
    "mean_reversion": """
        <h3>均值回归策略 (Mean Reversion)</h3>
        <p><strong>买入信号</strong>：价格触及布林带下轨（SMA20 - 2×标准差），且 RSI < 30。</p>
        <p><strong>卖出信号</strong>：
            <ul>
                <li>价格回归 SMA20（<em>Price Reached SMA20</em>）。</li>
                <li>RSI > 70（<em>RSI Overbought</em>）。</li>
            </ul>
        </p>
        <p><strong>参数</strong>：
            <ul>
                <li>SMA20：20日简单移动平均线</li>
                <li>布林带：20日周期，2倍标准差</li>
                <li>RSI：14日相对强弱指数，阈值30/70</li>
            </ul>
        </p>
    """,
    "momentum": """
        <h3>动量策略 (Momentum)</h3>
        <p><strong>买入信号</strong>：MACD线 上穿 MACD信号线，且 ADX > 25（确认趋势强度）。</p>
        <p><strong>卖出信号</strong>：
            <ul>
                <li>MACD线 下穿 MACD信号线（<em>MACD Cross Below</em>）。</li>
                <li>ADX < 25（<em>ADX Below 25</em>）。</li>
            </ul>
        </p>
        <p><strong>参数</strong>：
            <ul>
                <li>MACD：快线12日，慢线26日，信号线9日</li>
                <li>ADX：14日周期，阈值25</li>
            </ul>
        </p>
    """,
    "pattern": """
        <h3>形态策略 (Pattern)</h3>
        <p><strong>买入信号</strong>：价格形成双底（触及20日低点）后，突破20日高点，且 RSI < 70。</p>
        <p><strong>卖出信号</strong>：
            <ul>
                <li>价格跌破 SMA10（<em>Below SMA10</em>）。</li>
                <li>RSI > 70（<em>RSI Overbought</em>）。</li>
            </ul>
        </p>
        <p><strong>参数</strong>：
            <ul>
                <li>SMA10：10日简单移动平均线</li>
                <li>Low20：20日最低价</li>
                <li>High20：20日最高价</li>
                <li>RSI：14日相对强弱指数，阈值70</li>
            </ul>
        </p>
    """,
    "combined": """
        <h3>组合策略 (Combined)</h3>
        <p><strong>买入信号</strong>：SMA10 上穿 SMA20，且 RSI > 55。</p>
        <p><strong>卖出信号</strong>：
            <ul>
                <li>价格跌破买入价的5%（<em>Stop Loss</em>）。</li>
                <li>价格超过买入价的15%（<em>Take Profit</em>）。</li>
                <li>持仓达到15天（<em>Max Hold</em>）。</li>
            </ul>
        </p>
        <p><strong>参数</strong>：
            <ul>
                <li>SMA10：10日简单移动平均线</li>
                <li>SMA20：20日简单移动平均线</li>
                <li>RSI：14日相对强弱指数，阈值55</li>
                <li>止损：5%</li>
                <li>止盈：15%</li>
                <li>最大持仓：15天</li>
            </ul>
        </p>
    """,
}

# ========= 策略函数 ========= #

def strategy_breakout(df: pd.DataFrame) -> pd.DataFrame:
    """突破策略：价格突破20日高点，RSI过滤，SMA确认趋势"""
    df["Signal"] = 0
    df["Sell_Reason"] = np.nan
    buy_condition = (df["RSI"] < 70) & (df["SMA10"] > df["SMA20"])
    df.loc[buy_condition, "Signal"] = 1
    sell_condition_sma = (df["Close"] < df["SMA10"])
    sell_condition_spike = (df["Close"].pct_change() > 0.15)
    df.loc[sell_condition_sma, "Signal"] = -1
    df.loc[sell_condition_sma, "Sell_Reason"] = "Below SMA10"
    df.loc[sell_condition_spike & ~sell_condition_sma, "Signal"] = -1
    df.loc[sell_condition_spike & ~sell_condition_sma, "Sell_Reason"] = "Price Spike"
    return df

def strategy_trend_following(df: pd.DataFrame) -> pd.DataFrame:
    """趋势跟随策略"""
    df["Signal"] = 0
    df["Sell_Reason"] = np.nan
    buy_condition = (df["SMA10"] > df["SMA50"]) & (df["SMA10"].shift(1) <= df["SMA50"].shift(1)) & (df["RSI"] < 70)
    df.loc[buy_condition, "Signal"] = 1
    sell_condition_sma = (df["SMA10"] < df["SMA20"])
    sell_condition_rsi = (df["RSI"] > 70)
    df.loc[sell_condition_sma, "Signal"] = -1
    df.loc[sell_condition_sma, "Sell_Reason"] = "SMA10 Below SMA20"
    df.loc[sell_condition_rsi & ~sell_condition_sma, "Signal"] = -1
    df.loc[sell_condition_rsi & ~sell_condition_sma, "Sell_Reason"] = "RSI Overbought"
    return df

def strategy_mean_reversion(df: pd.DataFrame) -> pd.DataFrame:
    """均值回归策略：布林带超买/超卖，RSI确认"""
    df["BB_Upper"] = df["SMA20"] + 2 * df["Close"].rolling(20).std()
    df["BB_Lower"] = df["SMA20"] - 2 * df["Close"].rolling(20).std()
    df["Signal"] = 0
    df["Sell_Reason"] = np.nan
    buy_condition = (df["Close"] <= df["BB_Lower"]) & (df["RSI"] < 30)
    df.loc[buy_condition, "Signal"] = 1
    sell_condition_sma = (df["Close"] >= df["SMA20"])
    sell_condition_rsi = (df["RSI"] > 70)
    df.loc[sell_condition_sma, "Signal"] = -1
    df.loc[sell_condition_sma, "Sell_Reason"] = "Price Reached SMA20"
    df.loc[sell_condition_rsi & ~sell_condition_sma, "Signal"] = -1
    df.loc[sell_condition_rsi & ~sell_condition_sma, "Sell_Reason"] = "RSI Overbought"
    return df

def strategy_momentum(df: pd.DataFrame) -> pd.DataFrame:
    """动量策略：MACD金叉，ADX确认趋势强度"""
    exp1 = df["Close"].ewm(span=12, adjust=False).mean()
    exp2 = df["Close"].ewm(span=26, adjust=False).mean()
    df["MACD"] = exp1 - exp2
    df["MACD_Signal"] = df["MACD"].ewm(span=9, adjust=False).mean()
    high_diff = df["High"].diff()
    low_diff = df["Low"].diff()
    plus_dm = np.where((high_diff > low_diff) & (high_diff > 0), high_diff, 0)
    minus_dm = np.where((low_diff > high_diff) & (low_diff > 0), low_diff, 0)
    tr = pd.concat([
        (df["High"] - df["Low"]),
        (df["High"] - df["Close"].shift(1)).abs(),
        (df["Low"] - df["Close"].shift(1)).abs()
    ], axis=1).max(axis=1)
    df["Plus_DI"] = 100 * pd.Series(plus_dm).rolling(14).mean() / tr.rolling(14).mean()
    df["Minus_DI"] = 100 * pd.Series(minus_dm).rolling(14).mean() / tr.rolling(14).mean()
    dx = 100 * abs(df["Plus_DI"] - df["Minus_DI"]) / (df["Plus_DI"] + df["Minus_DI"])
    df["ADX"] = dx.rolling(14).mean()
    df["Signal"] = 0
    df["Sell_Reason"] = np.nan
    buy_condition = (df["MACD"] > df["MACD_Signal"]) & (df["MACD"].shift(1) <= df["MACD_Signal"].shift(1)) & (df["ADX"] > 25)
    df.loc[buy_condition, "Signal"] = 1
    sell_condition_macd = (df["MACD"] < df["MACD_Signal"])
    sell_condition_adx = (df["ADX"] < 25)
    df.loc[sell_condition_macd, "Signal"] = -1
    df.loc[sell_condition_macd, "Sell_Reason"] = "MACD Cross Below"
    df.loc[sell_condition_adx & ~sell_condition_macd, "Signal"] = -1
    df.loc[sell_condition_adx & ~sell_condition_macd, "Sell_Reason"] = "ADX Below 25"
    return df

def strategy_pattern(df: pd.DataFrame) -> pd.DataFrame:
    """形态策略：简化双底/双顶突破"""
    df["Signal"] = 0
    df["Sell_Reason"] = np.nan
    df["Low20"] = df["Close"].rolling(20).min()
    df["Is_Low"] = (df["Close"] == df["Low20"])
    df["Break_High20"] = (df["Close"] > df["High20"].shift(1))
    buy_condition = df["Is_Low"].shift(1) & df["Break_High20"] & (df["RSI"] < 70)
    df.loc[buy_condition, "Signal"] = 1
    sell_condition_sma = (df["Close"] < df["SMA10"])
    sell_condition_rsi = (df["RSI"] > 70)
    df.loc[sell_condition_sma, "Signal"] = -1
    df.loc[sell_condition_sma, "Sell_Reason"] = "Below SMA10"
    df.loc[sell_condition_rsi & ~sell_condition_sma, "Signal"] = -1
    df.loc[sell_condition_rsi & ~sell_condition_sma, "Sell_Reason"] = "RSI Overbought"
    return df

def strategy_combined(df: pd.DataFrame) -> pd.DataFrame:
    """组合策略：MA10上穿MA20 + RSI>55，卖出基于5%止损、15%止盈、15天持有"""
    df["Signal"] = 0
    df["Buy_Price"] = np.nan
    df["Hold_Days"] = 0
    df["Sell_Reason"] = np.nan
    buy_condition = (
        (df["SMA10"] > df["SMA20"]) & 
        (df["SMA10"].shift(1) <= df["SMA20"].shift(1)) & 
        (df["RSI"] > 55)
    )
    df.loc[buy_condition, "Signal"] = 1
    df.loc[buy_condition, "Buy_Price"] = df["Close"]
    df["In_Position"] = df["Signal"].eq(1).cumsum().shift(1).fillna(0).gt(0)
    df["Buy_Price"] = df["Buy_Price"].ffill()
    df["Hold_Days"] = df["In_Position"].groupby((df["Signal"] == 1).cumsum()).cumcount()
    stop_loss = df["In_Position"] & (df["Close"] < df["Buy_Price"] * 0.95)
    take_profit = df["In_Position"] & (df["Close"] > df["Buy_Price"] * 1.15)
    max_hold = df["In_Position"] & (df["Hold_Days"] >= 15)
    df.loc[stop_loss, "Signal"] = -1
    df.loc[stop_loss, "Sell_Reason"] = "Stop Loss"
    df.loc[take_profit & ~stop_loss, "Signal"] = -1
    df.loc[take_profit & ~stop_loss, "Sell_Reason"] = "Take Profit"
    df.loc[max_hold & ~stop_loss & ~take_profit, "Signal"] = -1
    df.loc[max_hold & ~stop_loss & ~take_profit, "Sell_Reason"] = "Max Hold"
    df = df.drop(columns=["In_Position", "Buy_Price", "Hold_Days"])
    return df

# ========= 策略映射 ========= #
STRATEGIES = {
    "breakout": strategy_breakout,
    "trend_following": strategy_trend_following,
    "mean_reversion": strategy_mean_reversion,
    "momentum": strategy_momentum,
    "pattern": strategy_pattern,
    "combined": strategy_combined,
}
